package com.baofu.paycore.manager.storage.impl;

import com.alibaba.dubbo.common.utils.CollectionUtils;
import com.baofu.paycore.common.exception.PayCoreBizException;
import com.baofu.paycore.common.exception.PayCoreManagerException;
import com.baofu.paycore.dal.mapper.RefundMapper;
import com.baofu.paycore.dal.mapper.SubRefundMapper;
import com.baofu.paycore.dal.model.RefundDO;
import com.baofu.paycore.dal.model.SubRefundDO;
import com.baofu.paycore.manager.converter.DO2BOConverter;
import com.baofu.paycore.manager.model.RefundBO;
import com.baofu.paycore.manager.model.SubRefundBO;
import com.baofu.paycore.manager.storage.Storage;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.system.commons.exception.BizServiceException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 退款主单处理manager
 * <p>
 * </p>
 * User: kwl Date: 2016/6/4 ProjectName: paycore Version: 5.0.0
 */
@Slf4j
@Service
public class RefundManager implements Storage<RefundDO> {

    /**
     * 退款主单Mapper
     */
    @Autowired
    private RefundMapper refundMapper;

    /**
     * 退款内部资金单Mapper
     */
    @Autowired
    private SubRefundMapper subRefundMapper;

    /**
     * 新增退款主单
     *
     * @param refundDO    退款主单信息
     */
    @Override
    public int addOrder(RefundDO refundDO) {

        log.debug("新增退款主单, parameter:{}",refundDO);
        int insertRows = refundMapper.insert(refundDO);
        if(1 != insertRows){
            log.error("新增退款主单失败, error:{}",PayCoreErrorCode.INSERT_DATA_NOT_NOE);
            throw new PayCoreManagerException(PayCoreErrorCode.INSERT_DATA_NOT_NOE);
        }
        return insertRows;
    }

    /**
     * 更新退款主单
     *
     * @param refundDO    退款主单信息
     */
    @Override
    public int modifyOrder(RefundDO refundDO) {

        log.debug("更新退款主单, parameter:{}",refundDO);
        int updateRows = refundMapper.updateStatusByPayNo(refundDO.getPayNo(),refundDO.getStatus());
        if(1 < updateRows){
            log.error("更新退款主单, error:{}",PayCoreErrorCode.UPDATE_DATA_NOT_ONE);
            throw new PayCoreManagerException(PayCoreErrorCode.UPDATE_DATA_NOT_ONE);
        }
        return updateRows;
    }

    /**
     * 废弃退款主单
     *
     * @param refundDO    退款主单信息
     */
    @Override
    public int deleteOrder(RefundDO refundDO) {
        return 0;
    }

    /**
     * 幂等性校验
     *
     * @param requestBizNo      请求流水号
     * @param requestOrderNo    请求订单号
     * @return                  退款订单信息
     */
    public RefundBO queryByReqInfo(String requestBizNo, String requestOrderNo){

        log.debug("退款幂等性校验, requestBizNo:{},requestOrderNo:{}", requestBizNo, requestOrderNo);

        RefundDO refundDO = refundMapper.selectByReqInfo(requestBizNo, requestOrderNo);
        return DO2BOConverter.refundDO2BOConverter(refundDO);
    }

    /**
     * 根据支付单号查询退款订单信息
     *
     * @param payNo    退款支付单号
     * @param status   指定查询状态
     * @return         退款订单信息
     */
    public RefundBO queryByPayNo(String payNo,String status){

        log.debug("根据退款支付单号查询退款订单信息, payNo:{}",payNo);

        RefundDO refundDO = refundMapper.selectByPayNo(payNo);
        if(null == refundDO){
            log.error("查询退款主单信息为空, payNo:{},error:{}",payNo,
                    PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
            throw new PayCoreBizException(PayCoreErrorCode.ORDER_IS_NOT_EXISTED);
        }

        //查询INIT状态的资金单
        List<SubRefundDO> subRefundDOs = subRefundMapper.selectByPayNo(payNo,
                status);
        if(CollectionUtils.isEmpty(subRefundDOs)){
            log.error("查询退款资金单信息为空, payNo:{},error:{}",payNo,
                    PayCoreErrorCode.SUB_ORDER_IS_NOT_EXISTED);
            throw new PayCoreBizException(PayCoreErrorCode.SUB_ORDER_IS_NOT_EXISTED);
        }

        List<SubRefundBO> subRefundBOs = subRefundDOs.stream().map(
                DO2BOConverter::subRefundDO2BOConverter).collect(Collectors.toList());

        RefundBO refundBO = DO2BOConverter.refundDO2BOConverter(refundDO);
        refundBO.setSubRefundBOs(subRefundBOs);

        return refundBO;
    }

    /**
     * 退款回调时，根据资金明细单号查询退款订单信息
     *
     * <p>
     * 退款回调时，根据回调返信息查询原退款订单信息，此时需要对回调返回的相关参数进行校验：
     * 1、回调单号校验：若查询原单信息不存在，则表明回调返回的订单号异常。
     * 2、原单状态校验：若原单状态为 SUCCESS或FAIL 时，表明原单已经回调处理完成，此次为重复回调
     *
     * 注：只查询非INIT状态的退款资金单
     * </p>
     *
     * @param payNo           退款支付单号
     * @param fundDetailNo    退款资金单号
     * @param amt             退款金额
     * @return                退款订单信息
     */
    public RefundBO queryOrigOrderByPayNo(String payNo, String fundDetailNo, long amt){

        log.debug("根据退款支付单号查询退款订单信息, payNo:{}",payNo);

        RefundDO refundDO = refundMapper.selectByPayNo(payNo);
        if(null == refundDO){

            log.error("退款回调返回单号校验失败,无法查到原退款单信息, payNo:{},error:{}",payNo,
                    PayCoreErrorCode.CALLBACK_PARAM_VALID_NOT_PASS);
            throw new PayCoreBizException(PayCoreErrorCode.CALLBACK_PARAM_VALID_NOT_PASS,
                    "退款回调返回单号校验失败,无法查到原退款单信息");
        }

        //回调原单状态判断
        if(StringUtils.equals(PayStatusEnums.SUCCESS.getCode(), refundDO.getStatus()) ||
                StringUtils.equals(PayStatusEnums.FAIL.getCode(), refundDO.getStatus())){

            log.error("原退款订单状态已结束，回调重复, error:{}", PayCoreErrorCode.CALLBACK_REPEAT_EXCEPTION);
            throw new PayCoreBizException(PayCoreErrorCode.CALLBACK_REPEAT_EXCEPTION);
        }

        //回调金额校验
        if(amt != refundDO.getAmt()){

            log.error("退款回调返回金额校验失败，金额与原单金额不符, error:{}",
                    PayCoreErrorCode.CALLBACK_PARAM_VALID_NOT_PASS);
            throw new PayCoreBizException(PayCoreErrorCode.CALLBACK_PARAM_VALID_NOT_PASS,
                    "退款回调返回金额校验失败，金额与原单金额不符");
        }

        //查询INIT状态的资金单
        List<SubRefundDO> subRefundDOs = subRefundMapper.selectByPayNo(payNo,
                PayStatusEnums.INIT.getCode());
        if(CollectionUtils.isEmpty(subRefundDOs)){
            log.error("回调查询退款资金单信息为空, payNo:{},error:{}",payNo,
                    PayCoreErrorCode.SUB_ORDER_IS_NOT_EXISTED);
            throw new PayCoreBizException(PayCoreErrorCode.SUB_ORDER_IS_NOT_EXISTED,
                    "回调查询退款资金单信息为空");
        }

        List<SubRefundBO> subRefundBOs = subRefundDOs.stream().map(
                DO2BOConverter::subRefundDO2BOConverter).collect(Collectors.toList());

        RefundBO refundBO = DO2BOConverter.refundDO2BOConverter(refundDO);
        refundBO.setSubRefundBOs(subRefundBOs);

        SubRefundBO subRefundBO = refundBO.getFundDetail(subRefundBOs, fundDetailNo);
        if(null == subRefundBO){

            log.error("退款回调返回资金单号校验失败,无法匹配原退款资金单信息, fundDetailNo:{},error:{}",fundDetailNo,
                    PayCoreErrorCode.CALLBACK_PARAM_VALID_NOT_PASS);
            throw new PayCoreBizException(PayCoreErrorCode.CALLBACK_PARAM_VALID_NOT_PASS,
                    "退款回调返回资金单号校验失败,无法匹配原退款资金单信息");
        }

        return refundBO;
    }

    /**
     * 根据资金明细单号查询退款订单信息
     *
     * @param fundDetailNo    资金明细单号
     * @return         退款订单信息
     */
    public RefundBO queryByBankFundDetailNo(String fundDetailNo) {
        SubRefundDO subRefundDO = subRefundMapper.selectByFundDetailNo(fundDetailNo);
        return queryByPayNo(subRefundDO.getPayNo(),PayStatusEnums.INIT.getCode());
    }

    public void lockAndValid(String payNo) {
        RefundDO refundDO = refundMapper.selectByPayNoForUpdate(payNo);
        if(!refundDO.getStatus().equals(PayStatusEnums.INIT.getCode())){
            throw new BizServiceException(PayCoreErrorCode.ORDER_STATUS_NOT_INIT);
        }
    }

    /**
     * 根据订单号和交易流水号查询退款
     *
     * @return 交易对象
     */
    public RefundDO queryByOrderNo(String requestOrderNo, String requestBizNo, Date requestDate, String requestSystem){
        RefundDO refundDO = refundMapper.selectByOrderNo(requestOrderNo, requestBizNo, requestDate, requestSystem);
        if(refundDO == null){
            log.error("查询结果集为空:{} {} {}",requestOrderNo,requestBizNo,requestDate,requestSystem);
            throw new BizServiceException(PayCoreErrorCode.EMPTY_QUERY_RESULT);
        }
        return refundDO;
    }
}
